#! /usr/bin/env python
"""
Copyright 2008 Olivier Belanger

This file is part of Ounk.

Ounk is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Ounk is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Ounk.  If not, see <http://www.gnu.org/licenses/>.
"""

import sys, os, time, traceback, types, string, threading, inspect, exceptions
import keyword
import wx
import wx.stc  as  stc
from wx.lib.splitter import MultiSplitterWindow
from wx.lib.wordwrap import wordwrap
from subprocess import Popen
import  wx.lib.dialogs
import wx.aui
import wx.html
import wx.py as py
import wx.richtext as rt
from ounk.MyPrinter import Printer
from ounk.ounklib import *

# PATHS #
CURRENTDIR = os.path.dirname(sys.argv[0])
if '/Ounk.app' not in os.getcwd():
    if CURRENTDIR:
        os.chdir(CURRENTDIR)    
    RESOURCES_PATH = os.path.join(os.getcwd(), 'ounk')
    TEMPLATES_PATH = os.path.join(os.getcwd(), 'files', 'templates')
    DOC_PATH = os.path.join(os.getcwd(), 'doc')
    SETTINGS_PATH = os.path.join(os.getcwd(), 'settings')           
    TEMP_PATH = os.path.join(os.path.expanduser('~'), '.ounk')           
else:
    RESOURCES_PATH = os.getcwd()
    TEMPLATES_PATH = os.path.join('..', '..', '..', 'files', 'templates')
    DOC_PATH = os.path.join('..', '..', '..', 'doc')
    SETTINGS_PATH = os.path.join('..', '..', '..', 'settings')
    TEMP_PATH = os.path.join(os.path.expanduser('~'), '.ounk')

sys.setcheckinterval(50)

conf = {}

def _settings(file):
    f = open(os.path.join(SETTINGS_PATH, file), 'r')
    for line in f.readlines():
        if line[0] != '#':
            l = line.strip().split('=')
            if len(l) > 1:
                conf[l[0].strip()] = eval(l[1].strip())
                
if wx.Platform == '__WXMSW__':
    faces = { 'size' : 10,
              'size2': 8}
    _settings('config_win32.py')
elif wx.Platform == '__WXMAC__':
    faces = { 'size' : 12,
              'size2': 10}
    _settings('config_darwin.py')
else:
    faces = { 'size' : 10,
              'size2': 8}
    _settings('config_linux.py')

execfile(os.path.join(SETTINGS_PATH, 'Styles.py'))    

styles = STYLES.keys()

for key, value in STYLES[conf['preferedStyle']].items():
    faces[key] = value   

exceptionsList = [ex for ex in dir(exceptions) if ex[0] != '_']

NAME = 'Ounk'
VERSION = '0.2 (beta)'
CURRENTDIR = os.getcwd()
COLOUR = wx.Colour(190, 190, 190, 255)
        
class ControlPanel(wx.Panel):
    def __init__(self, parent, infoView, mainPanel, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, size=size, style = wx.EXPAND | wx.SUNKEN_BORDER)

        self.mainPanel = mainPanel
        self.procNum = 1

        sizer = wx.BoxSizer(wx.VERTICAL)
        runsizer = wx.BoxSizer(wx.VERTICAL)
        
        self.btn = wx.Button(self, -1, "Run")
        self.Bind(wx.EVT_BUTTON, self.runButton, self.btn)        
        runsizer.Add(self.btn, 0, wx.TOP | wx.ALIGN_CENTER, 4)

        self.notebook = wx.Notebook(self, -1, size=size, style=wx.BK_DEFAULT | wx.EXPAND)
        self.tree = FunctionTree(self.notebook, infoView,  self.mainPanel, size=size)
        self.project = ProjectTree(self.notebook, self.mainPanel, size=size)
        self.notebook.AddPage(self.tree, "Library")
        self.notebook.AddPage(self.project, "Projects")

        sizer.Add(runsizer, 0, wx.ALIGN_CENTER, 2)
        sizer.Add(self.notebook, 1, wx.EXPAND)
        self.SetSizer(sizer)
        
    def running(self):
        if not self.mainPanel.editor.procNum:  
            self.mainPanel.editor.procNum = self.procNum
        processNumber(self.mainPanel.editor.procNum) 
        if self.mainPanel.editor.style == 'Csound':
            playCSD(self.mainPanel.editor.path)
        else:
            self.mainPanel.editor.shel.clearHistory() 
            self.mainPanel.editor.shel.clear()
            self.mainPanel.editor.shel.interp.locals = {}
            self.mainPanel.editor.shel.push('from ounk.ounklib import *', True)
            self.mainPanel.editor.shel.prompt()
            script = '\n'.join([l for l in self.mainPanel.editor.GetText().splitlines(True)]) + '\n'
            self.mainPanel.editor.shel.Execute(script)
            if not self.mainPanel.mainFrame.onVerbose:
                self.mainPanel.editor.shel.clear()        
                self.mainPanel.editor.shel.prompt()
            self.checkErrors()
        self.procNum += 1
        self.mainPanel.editor.startTimer()

    def allRun(self, state):
        currentEditor = self.mainPanel.editor
        for i in range(self.mainPanel.notebook.GetPageCount()):
            self.mainPanel.editor = self.mainPanel.notebook.GetPage(i)
            if self.mainPanel.editor.inRun and not state:
                self.mainPanel.editor.stopTimer()
            elif not self.mainPanel.editor.inRun and state:
                self.running()
            else:
                pass
        self.mainPanel.editor = currentEditor 
        self.checkButton()

    def checkErrors(self):
        linelist = self.mainPanel.editor.shel.GetText().split('\n')
        errorlist = []
        for line in linelist:
            try:
                if line.split()[0][0:-1] in exceptionsList:
                    errorlist.append(linelist.index(line))
            except:
                pass
        if len(errorlist):
            num = self.mainPanel.editor.procNum
            errorlog = open(os.path.join(TEMP_PATH, 'errlog%d.txt' % num), "w")
            for i in errorlist: 
                errorlog.write(linelist[i-2] + '\n')
                errorlog.write(linelist[i-1] + '\n')
                errorlog.write(linelist[i] + '\n\n')
            errorlog.close()
            self.mainPanel.mainFrame.openErrLog(None)
        
    def checkButton(self):
        if self.mainPanel.editor.inRun and self.btn.GetLabel() == 'Run':
            self.btn.SetLabel('Stop')
        elif not self.mainPanel.editor.inRun and self.btn.GetLabel() == 'Stop':   
            self.btn.SetLabel('Run')
            try:
                self.mainPanel.editor.shel.push('onStop()', True)
            except NameError:
                pass
                
    def runButton(self, evt):
        if self.mainPanel.editor.inRun:
            self.mainPanel.editor.stopTimer()
        else:   
            self.running()
        self.checkButton()
        
class MainFrame(wx.Frame):
    def __init__(self, parent, ID, title, pos=wx.DefaultPosition, size=wx.DefaultSize, 
                 style=wx.DEFAULT_FRAME_STYLE):
        wx.Frame.__init__(self, parent, ID, title, pos, size, style)

        self.screenSize = size
        self.panel = MainPanel(self, size=size)
        self.printer = Printer(self)
        
        if wx.Platform == '__WXMAC__':
            self.MacSetMetalAppearance(True)

        self.allRunning = False        
        self.onVerbose = True
        
        self.menuBar = wx.MenuBar()
        menu1 = wx.Menu()
        submenu1 = wx.Menu()
        submenu1.Append(110, "New\tCtrl+N", "Creates a new page")
        subId = 1000
        files = [file for file in os.listdir(TEMPLATES_PATH) if file.split('.')[1] == 'py']
        if files:
            submenu1.InsertSeparator(1)
            for file in files:
                submenu1.Append(subId, file, "Opens %s template as a new page" % file.split('.')[0])
                subId += 1
        menu1.AppendMenu(999, "New...", submenu1, "Creates a new page")
        menu1.InsertSeparator(1)
        menu1.Append(100, "Open\tCtrl+O", "Opens an existing file")
        menu1.Append(112, "Open Project\tShift+Ctrl+O", "Opens a project folder. Path of the folder will be added to sys.path module")
        self.submenu2 = wx.Menu()
        subId2 = 2000
        recentFiles = []
        filename = os.path.join(TEMP_PATH,'.recent.txt')
        if os.path.isfile(filename):
            f = open(filename, "r")
            for line in f.readlines():
                recentFiles.append(line)
            f.close()    
        if recentFiles:
            for file in recentFiles:
                self.submenu2.Append(subId2, file)
                subId2 += 1
        menu1.AppendMenu(998, "Open Recent...", self.submenu2)        
        menu1.InsertSeparator(5)
        menu1.Append(101, "Save\tCtrl+S")
        menu1.Append(102, "Save as...\tShift+Ctrl+S")
        menu1.Append(111, "Close\tCtrl+W", "Closes front window")
        menu1.InsertSeparator(9)
        menu1.Append(109, "Open .csd file\tCtrl+T", "Shows the .csd file generated by the visible script")
        menu1.Append(127, "Open Csound log\tShift+Ctrl+T", "Opens the Csound log file for the last launched script")
        menu1.Append(134, "Open Errors log\tShift+Ctrl+Y", "Opens the Errors log file for the visible script")
        menu1.InsertSeparator(13)
        menu1.Append(128, "Preferences...\tCtrl+;", "Customize Ounk behavior")
        menu1.InsertSeparator(15)
        menu1.Append(123, "Page Setup...", "Opens the printer page setup dialog")
        menu1.Append(124, "Print Preview...", "Opens the print preview window")
        menu1.Append(125, "Print...\tCtrl+P")
        menu1.InsertSeparator(19)
        menu1.Append(129, "Quit\tCtrl+Q")  
        self.menuBar.Append(menu1, 'File')

        menu2 = wx.Menu()
        menu2.Append(103, "Collapse/Expand\tShift+Ctrl+F", "Collapse or Expand all folds")
        menu2.Append(108, "Un/Comment Selection\tCtrl+J", "Comments or Uncomments selected lines")
        menu2.Append(114, "Show AutoCompletion\tCtrl+K", "Checks for autoCompletion possibilities and opens popup menu")
        menu2.Append(121, "Insert file path...\tCtrl+L", "Opens standard dialog and insert chosen file path at the current position")
        menu2.Append(122, "Find...\tCtrl+F", "Opens Find and Replace standard dialog")
        menu2.InsertSeparator(5)
        menu2.Append(115, "Open Chords file", "Opens Chords file to modify it")   
        menu2.Append(135, "Open Scales file", "Opens Chords file to modify it")   
        menu2.InsertSeparator(8)
        menu2.Append(105, "Show commands\tCtrl+E", "Shows parsed lines in the interpreter", wx.ITEM_CHECK)
        menu2.Check(105, True) 
        menu2.Append(131, "Clear text in the interpreter\tCtrl+I", "Erases all text in the interpreter window")
        menu2.Append(132, "Zoom in interpreter\tShift+Ctrl+=", "increases text font in the interpreter")
        menu2.Append(133, "Zoom out interpreter\tShift+Ctrl+-", "decreases text font in the interpreter")
        self.menuBar.Append(menu2, 'Code')
        
        menu3 = wx.Menu()
        menu3.Append(104, "Run\tCtrl+R", "Runs the script in the visible page")
        menu3.Append(113, "Run all\tShift+Ctrl+R", "Runs all scripts opened in the editor panel")
        menu3.InsertSeparator(2)
        if '/Ounk.app' in os.getcwd():
            menu3.Append(106, "Open Console (OS X only)\tCtrl+Y", "Opens Console application to see Python and Csound StdOut")
            self.Bind(wx.EVT_MENU, self.console, id=106)
        menu3.Append(107, "Kill all Csound processes\tCtrl+.", "Kills all Csound processes running on the system")
        submenu3 = wx.Menu()
        submenu3.Append(3001, "0", "Fastest as possible!", wx.ITEM_RADIO)
        submenu3.Append(3002, "10", "Pretty fast!", wx.ITEM_RADIO)
        submenu3.Append(3003, "20", "Moderatly fast!", wx.ITEM_RADIO)
        submenu3.Append(3004, "50", "A little bit slow!", wx.ITEM_RADIO)
        submenu3.Append(3005, "100", "Timming is not a priority!", wx.ITEM_RADIO)
        submenu3.Append(3006, "200", "Very slow!", wx.ITEM_RADIO)
        submenu3.Check(3004, True)
        menu3.AppendMenu(3000, "Python Timer Responsiveness...", submenu3, "Ajust the check interval of the Python interpreter")
        self.menuBar.Append(menu3, 'Process')

        menu5 = wx.Menu()
        stId = 500
        for st in styles:
            menu5.Append(stId, st, "", wx.ITEM_RADIO)
            if st == conf['preferedStyle']: menu5.Check(stId, True)
            stId += 1
        menu5.InsertSeparator(len(styles))
        menu5.Append(130, "Edit styles sheet", "Opens styles file to add custom style to the menu")
        self.menuBar.Append(menu5, 'Styles')
        
        menu4 = wx.Menu()
        menu4.Append(120, "Overview", "Shows ounklib functions overview. It's the manual starting page!")
        menu4.InsertSeparator(1)
        manId = 10000
        keylist = LIB_INFO.keys()
        for key in sorted(keylist):
            submenuMan = wx.Menu()
            menu4.AppendMenu(-1, key, submenuMan)
            list = sorted(LIB_INFO[key].keys())
            if list:
                for f in list:
                    submenuMan.Append(manId, f)
                    manId += 1
        #menu4.InsertSeparator(manId-10000+19)            
        self.manItem = wx.MenuItem(menu4, 126, "Open man page\tCtrl+U", "Opens the manual page for the selected keyword")
        menu4.AppendItem(self.manItem)
        self.manItem.Enable(False)
        self.menuBar.Append(menu4, 'Manual')
        
        # Make a Help menu
        menu = wx.Menu()
        helpItem = menu.Append(-1, '&About %s %s' % (NAME, VERSION), 'wxPython RULES!!!')
        wx.App.SetMacAboutMenuItemId(helpItem.GetId())
        self.menuBar.Append(menu, '&Help')
        
        self.SetMenuBar(self.menuBar)

        self.Bind(wx.EVT_MENU, self.new, id=110)        
        self.Bind(wx.EVT_MENU, self.open, id=100)
        self.Bind(wx.EVT_MENU, self.openProject, id=112)
        self.Bind(wx.EVT_MENU, self.save, id=101)
        self.Bind(wx.EVT_MENU, self.saveas, id=102)
        self.Bind(wx.EVT_MENU, self.delete, id=111)
        self.Bind(wx.EVT_MENU, self.fold, id=103)
        self.Bind(wx.EVT_MENU, self.runner, id=104)
        self.Bind(wx.EVT_MENU, self.runnerAll, id=113)
        self.Bind(wx.EVT_MENU, self.verbose, id=105)
        self.Bind(wx.EVT_MENU, self.clearText, id=131)
        self.Bind(wx.EVT_MENU, self.zoomIn, id=132)
        self.Bind(wx.EVT_MENU, self.zoomOut, id=133)
        self.Bind(wx.EVT_MENU, self.killCsound, id=107)
        self.Bind(wx.EVT_MENU, self.onHelpAbout, helpItem)
        self.Bind(wx.EVT_MENU, self.OnComment, id=108)    
        self.Bind(wx.EVT_MENU, self.openCsd, id=109)
        self.Bind(wx.EVT_MENU, self.openCsdLog, id=127)
        self.Bind(wx.EVT_MENU, self.openErrLog, id=134)
        self.Bind(wx.EVT_MENU, self.OnPageSetup, id=123)
        self.Bind(wx.EVT_MENU, self.openPrefs, id=128)
        self.Bind(wx.EVT_MENU, self.OnPrintPreview, id=124)
        self.Bind(wx.EVT_MENU, self.OnPrint, id=125)
        self.Bind(wx.EVT_MENU, self.openFunctionMan, id=126)
        
        if subId > 1000:
            for i in range(1000,subId):
                self.Bind(wx.EVT_MENU, self.openTemplate, id=i)
        if subId2 > 2000:
            for i in range(2000,subId2):
                self.Bind(wx.EVT_MENU, self.openRecent, id=i) 
        if manId > 10000:
            for i in range(10000, manId):
                self.Bind(wx.EVT_MENU, self.openPageMan, id=i)
        for i in range(500, stId):
            self.Bind(wx.EVT_MENU, self.changeStyle, id=i)
        for i in range(3001,3007):
            self.Bind(wx.EVT_MENU, self.adjustResponse, id=i)
        self.Bind(wx.EVT_MENU, self.autoComp, id=114) 
        self.Bind(wx.EVT_MENU, self.insertPath, id=121)
        self.Bind(wx.EVT_MENU, self.showFind, id=122)
        self.Bind(wx.EVT_MENU, self.chords, id=115)
        self.Bind(wx.EVT_MENU, self.scales, id=135)
        
        self.Bind(wx.EVT_MENU, self.showOverview, id=120)
        self.Bind(wx.EVT_MENU, self.openStyles, id=130)
        self.Bind(wx.EVT_SIZE, self.changedSize)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_MENU, self.OnClose, id=129)
        #self.sb = wx.StatusBar(self, -1)
        #self.SetStatusBar(self.sb)

        if projectsToOpen:
            for p in projectsToOpen:
                self.panel.controlPanel.project.loadProject(p)
                sys.path.append(p)
                projectPath(p)
            
        if filesToOpen:
            for f in filesToOpen:
                self.panel.addPage(f)
                
    ### Editor functions ###
    def OnComment(self, evt):
        self.panel.editor.OnComment()

    def fold(self, event):
        self.panel.editor.FoldAll()

    def autoComp(self, evt):
        win = self.FindFocus()
        try:
            win.showAutoComp()
        except AttributeError:
            pass

    def showFind(self, evt):
        self.panel.editor.OnShowFindReplace()

    def insertPath(self, evt):
        dlg = wx.FileDialog(self, message="Choose a file", defaultDir=os.getcwd(),
            defaultFile="", style=wx.OPEN | wx.MULTIPLE)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPaths()
            text = str(path[0])
            self.panel.editor.ReplaceSelection("'" + text + "'")
        dlg.Destroy()

    ### Printing ###        
    def OnPrint(self, event):
        lineCount = self.panel.editor.GetLineCount()
        text = [self.panel.editor.GetLine(i) for i in range(lineCount)]
        name = os.path.split(self.panel.editor.path)[1]
        self.printer.OnPrint(text, name)

    def OnPrintPreview(self, event):
        lineCount = self.panel.editor.GetLineCount()
        text = [self.panel.editor.GetLine(i) for i in range(lineCount)]
        name = os.path.split(self.panel.editor.path)[1]
        self.printer.OnPrintPreview(text, name)

    def OnPageSetup(self, event):
        self.printer.OnPageSetup()

    ### Styles ###        
    def changeStyle(self, evt):
        menu = self.GetMenuBar()
        id = evt.GetId()
        st = menu.FindItemById(id).GetLabel() 
        for key, value in STYLES[st].items():
            faces[key] = value 
        for i in range(self.panel.notebook.GetPageCount()):
            ed = self.panel.notebook.GetPage(i)
            ed.setStyle() 
            ed.shel.setStyle()
        self.panel.infoView.setStyle()
        self.panel.controlPanel.tree.setStyle()
        self.panel.controlPanel.project.setStyle()        

    def openStyles(self, evt):
        self.panel.addPage(os.path.join(SETTINGS_PATH,'Styles.py'))

    ### Open chords and scales files ###            
    def chords(self, evt):
        self.panel.addPage(os.path.join(SETTINGS_PATH,'chords.py'))

    def scales(self, evt):
        self.panel.addPage(os.path.join(SETTINGS_PATH,'scales.py'))

    ### Open Prefs ang Logs ###        
    def openPrefs(self, evt):
        if wx.Platform == '__WXMSW__':
            pref = 'config_win32.py'
        elif wx.Platform == '__WXMAC__':
            pref = 'config_darwin.py'
        else:
            pref = 'config_linux.py'
        self.panel.addPage(os.path.join(SETTINGS_PATH, pref))
    
    def openCsd(self, event):
        num = self.panel.editor.procNum
        f = open(os.path.join(TEMP_PATH,'temp%d.csd' % num), "r")
        msg = f.read()
        f.close()
        self.csdView = LogPage(self, "Csound file", pos=(20,20), 
                                  size=(self.screenSize[0]*2/3,self.screenSize[1]-100), msg=msg)
        
    def openCsdLog(self, event): 
        f = open(os.path.join(TEMP_PATH,'csoundlog.txt'), "r")
        msg = f.read()
        f.close()
        self.csdLogView = LogPage(self, "Csound log", pos=(40,20), 
                                     size=(self.screenSize[0]*2/3,self.screenSize[1]-100), msg=msg)

    def openErrLog(self, event):
        num = self.panel.editor.procNum
        file = os.path.join(TEMP_PATH,'errlog%d.txt' % num)
        if os.path.isfile(file):
            f = open(file, "r")
            msg = f.read()
            f.close()
            self.errView = LogPage(self, "Error log", pos=(60,20), 
                                      size=(self.screenSize[0]*2/3,self.screenSize[1]-100), msg=msg)

    ### Manual ###
    def showOverview(self, event): 
        try:
            self.man.Destroy()
        except:
            pass
        self.man = ManualPage(self, -1, "Functions overview", size=(800, 600), 
                              style = wx.DEFAULT_FRAME_STYLE)
        self.man.Show(True)

    def openPageMan(self, event):
        menu = self.GetMenuBar()
        try:
            self.man.Destroy()
        except:
            pass
        id = event.GetId()
        category = ''
        file = menu.FindItemById(id).GetLabel()  
        for key in LIB_INFO.keys():
            for func in LIB_INFO[key].keys():
                if func == file:
                    category = key
                    break
        self.man = ManualPage(self, -1, "Manual: %s" % file, size=(800, 600), 
                              style = wx.DEFAULT_FRAME_STYLE)
        self.man.openPage(os.path.join(DOC_PATH, 'html_man/Ounk_man', file))             
        self.man.Show(True)
 
    def openFunctionMan(self, event):
        try:
            self.man.Destroy()
        except:
            pass
        category = ''
        file = self.panel.editor.GetSelectedText() 
        for key in LIB_INFO.keys():
            for func in LIB_INFO[key].keys():
                if func == file:
                    category = key
                    break
        self.man = ManualPage(self, -1, "Manual: %s" % file, size=(800, 600), 
                              style = wx.DEFAULT_FRAME_STYLE)
        self.man.openPage(os.path.join(DOC_PATH, 'html_man/Ounk_man', file)) 
        self.man.Show(True)

    def enableFunctionManual(self, state):
        self.manItem.Enable(state)

    ### New / Open / Save / Delete ###                
    def new(self, event):
        self.panel.addNewPage()

    def newRecent(self, file):
        filename = os.path.join(TEMP_PATH,'.recent.txt')
        try:
            f = open(filename, "r")
            lines = [line[:-1] for line in f.readlines()]
            f.close()
        except:
            lines = []
        if not file in lines:
            f = open(filename, "w")
            lines.insert(0, file)
            if len(lines) > 10:
                lines = lines[0:10]
            for line in lines:
                f.write(line + '\n')
            f.close()    

        subId2 = 2000
        recentFiles = []
        f = open(filename, "r")
        for line in f.readlines():
            recentFiles.append(line)
        f.close()    
        if recentFiles:
            for item in self.submenu2.GetMenuItems():
                self.submenu2.DeleteItem(item)
            for file in recentFiles:
                self.submenu2.Append(subId2, file)
                subId2 += 1
        
    def open(self, event):
        dlg = wx.FileDialog(self, message="Choose a file", defaultDir=os.getcwd(),
            defaultFile="", style=wx.OPEN | wx.MULTIPLE)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPaths()
            for file in path:          
                self.panel.addPage(file)
                self.newRecent(file)
        dlg.Destroy()
        
    def openProject(self, event):
        dlg = wx.DirDialog(self, message="Choose a project folder", defaultPath=os.getcwd(),
                           style=wx.DD_DEFAULT_STYLE)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.folder = path
            self.panel.controlPanel.project.loadProject(self.folder)
        dlg.Destroy()
        sys.path.append(path)
        projectPath(path)
        
    def openTemplate(self, event):
        menu = self.GetMenuBar()
        id = event.GetId()
        file = menu.FindItemById(id).GetLabel()
        self.panel.addNewPage()
        self.panel.editor.LoadFile(os.path.join(TEMPLATES_PATH, file))
        self.panel.onLoadExample(file)
        self.panel.SetTitle('Ounk Editor')
        self.panel.editor.path = ''
        self.panel.editor.saveMark = False
        
    def openRecent(self, event):
        menu = self.GetMenuBar()
        id = event.GetId()
        file = menu.FindItemById(id).GetLabel()
        self.panel.addPage(file[:-1])
        
    def save(self, event):
        if not self.panel.editor.path:
            self.saveas(None)
        else:
            self.panel.editor.saveMyFile(self.panel.editor.path)
            self.SetTitle(self.panel.editor.path)
        
    def saveas(self, event):
        dlg = wx.FileDialog(self, message="Save file as ...", defaultDir=os.path.expanduser('~'),
            defaultFile="", style=wx.SAVE)
        dlg.SetFilterIndex(0)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.panel.editor.path = path
            self.panel.editor.setStyle()
            self.panel.editor.SetCurrentPos(0)
            self.panel.editor.AddText(" ")
            self.panel.editor.DeleteBackNotLine()
            self.panel.editor.saveMyFile(path)
            self.SetTitle(self.panel.editor.path)
            self.panel.notebook.SetPageText(self.panel.notebook.GetSelection(), 
                                            os.path.split(path)[1])
            self.newRecent(path)
        dlg.Destroy()

    def delete(self, event):
        action = self.panel.editor.close()
        if action == 'delete':
            self.panel.deletePage()
        else:
            pass
            
    ### Run actions ###
    def runner(self, event):
        self.panel.controlPanel.runButton(None)
        
    def runnerAll(self, event):
        if not self.allRunning:
            self.panel.controlPanel.allRun(True)
            self.allRunning = True
        else:    
            self.panel.controlPanel.allRun(False)
            self.allRunning = False

    ### Interpeter ###        
    def verbose(self, event):
        if self.onVerbose: self.onVerbose = False
        else: self.onVerbose = True

    def clearText(self, event):
        self.panel.editor.shel.clear()
        self.panel.editor.shel.interp.locals = {}
        self.panel.editor.shel.push('from ounk.ounklib import *', True)
        processNumber(1)
        self.panel.editor.shel.prompt()
        
    def zoomIn(self, event):
        self.panel.editor.shel.SetZoom(self.panel.editor.shel.GetZoom()+1)

    def zoomOut(self, event):
        self.panel.editor.shel.SetZoom(self.panel.editor.shel.GetZoom()-1)

    ### Misc ###
    def console(self, event):
        os.system("open /Applications/Utilities/Console.app")
        
    def killCsound(self, event):
        if wx.Platform == '__WXMSW__':
            os.system('taskkill /F /IM csound.exe')
        else:
            os.system("killall csound")

    def changedSize(self, event):
        self.panel.resize(self.GetSize())
        event.Skip()
    
    def adjustResponse(self,event):
        d = {3001: 0, 3002: 10, 3003: 20, 3004: 50, 3005: 100, 3006: 200}
        id = event.GetId()    
        sys.setcheckinterval(d[id])
        
    ### About ###        
    def onHelpAbout(self, evt):
        # First we create and fill the info object
        info = wx.AboutDialogInfo()
        info.Name = NAME
        info.Version = VERSION
        #info.Icon = wx.Icon(os.path.join(RESOURCES_PATH, 'cochon.png'), wx.BITMAP_TYPE_PNG)
        info.Copyright = "(C) 2008 Olivier Belanger"
        info.Description = wordwrap(
            "Ounk is a Python audio scripting environment that uses Csound as its engine.\n\n"

            "It can be used for a variety of tasks such as composing, sound design, "
            "live performances and much more. In addition to providing powerful "
            "synthesis and sampling capabilities, it supports MIDI and "
            "Open Sound Control protocols.\n\n"
            
            "Special thanks to Nathanael Lecaude and Jean-Michel Dumas\n",
            350, wx.ClientDC(self))
        info.WebSite = ("http://code.google.com/p/ounk", "Ounk home page")

        wx.AboutBox(info)   

    ### On Quit Application ###
    def OnClose(self, event): 
        stopCsound()
        files = [file for file in os.listdir(TEMP_PATH) if 'temp' in file]
        files.extend([file for file in os.listdir(TEMP_PATH) if 'errlog' in file])
        for f in files:
            os.remove(os.path.join(TEMP_PATH, f))
        try:
            os.remove(os.path.join(TEMP_PATH, 'csoundlog.txt'))
        except:
            pass
        self.panel.OnQuit()
        self.Destroy()
        
class MainPanel(wx.Panel):
    def __init__(self, parent, size=(1200,800), style=wx.SUNKEN_BORDER):
        wx.Panel.__init__(self, parent, size=(1200,800), style=wx.SUNKEN_BORDER)

        self.X, self.Y = size[0], size[1]
        self.mainFrame = parent
        
        mainBox = wx.BoxSizer(wx.HORIZONTAL) 

        self.mainSplitter = MultiSplitterWindow(self, size=size, 
                                        style=wx.SP_LIVE_UPDATE | wx.SP_3DSASH | wx.SUNKEN_BORDER)
        
        self.splitterX = self.X-(self.X*.15)
        self.splitter2 = MultiSplitterWindow(self.mainSplitter, size=(self.splitterX, self.Y), 
                                        style=wx.SP_LIVE_UPDATE | wx.SP_3DSASH | wx.SUNKEN_BORDER)
        self.splitter2.SetOrientation(wx.VERTICAL)
        self.splitter = MultiSplitterWindow(self.splitter2, size=(self.splitterX, self.Y), 
                                        style=wx.SP_LIVE_UPDATE | wx.SP_3DSASH | wx.SUNKEN_BORDER)

        self.notebook = MyNotebook(self.splitter)
        
        self.infoView = InfoView(self.splitter, -1, size=(0, -1), style=wx.SUNKEN_BORDER)
        self.controlPanel = ControlPanel(self.mainSplitter, self.infoView, self, size=(0, -1))
        self.editor = Editor(self.notebook, -1, size=(0, -1), infoView=self.infoView, 
                         setTitle=self.SetTitle, getTitle=self.GetTitle, splitter2=self.splitter2, 
                         checkButton=self.controlPanel.checkButton, 
                         enableFunctionManual=self.mainFrame.enableFunctionManual)
        self.notebook.AddPage(self.editor, "Untitled")

        self.mainSplitter.AppendWindow(self.controlPanel, self.X*.15)
        self.splitter2.AppendWindow(self.splitter, self.Y*0.85)
        self.splitter2.AppendWindow(self.editor.shel, self.Y*0.25)
        self.mainSplitter.AppendWindow(self.splitter2, self.X*.75)
        mainBox.Add(self.mainSplitter, 1, wx.EXPAND)
        self.SetSizer(mainBox)

        self.splitter.SetBackgroundColour(COLOUR)
        self.splitter2.SetBackgroundColour(COLOUR)
        
        self.splitter.AppendWindow(self.notebook, self.splitterX*0.7)
        self.editor.EmptyUndoBuffer()
        self.editor.LoadFile(os.path.join(RESOURCES_PATH,'default.py'))
        self.editor.SetFocus()

        self.splitter.AppendWindow(self.infoView, self.splitterX*0.3)

        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.onPageChange)
        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.onSashChanging)
        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.onClosePage)

    def addNewPage(self):
        self.splitter2.Hide()
        editor = Editor(self.notebook, -1, size=(self.splitterX*0.15, -1), infoView=self.infoView, 
                        setTitle=self.SetTitle, getTitle=self.GetTitle, splitter2=self.splitter2, 
                        checkButton=self.controlPanel.checkButton, 
                        enableFunctionManual=self.mainFrame.enableFunctionManual)
        self.notebook.AddPage(editor, "Untitled", True)
        self.splitter2.DetachWindow(self.splitter2.GetWindow(1))
        self.splitter2.InsertWindow(1, editor.shel)
        self.splitter2.Show()
        self.editor = editor
        
    def addPage(self, file):
        self.splitter2.Hide()
        editor = Editor(self.notebook, -1, size=(self.splitterX*0.15, -1), infoView=self.infoView, 
                        setTitle=self.SetTitle, getTitle=self.GetTitle, splitter2=self.splitter2, 
                        checkButton=self.controlPanel.checkButton, 
                        enableFunctionManual=self.mainFrame.enableFunctionManual)
        label = os.path.split(file)[1].split('.')[0]      
        self.notebook.AddPage(editor, label, True)
        self.splitter2.DetachWindow(self.splitter2.GetWindow(1))
        self.splitter2.InsertWindow(1, editor.shel)
        self.splitter2.Show()
        editor.LoadFile(file)
        editor.path = file
        editor.setStyle()
        self.editor = editor
        self.SetTitle(file)

    def onClosePage(self, evt):
        ed = self.notebook.GetPage(self.notebook.GetSelection())
        ed.Close()

    def deletePage(self):  
        ed = self.notebook.GetPage(self.notebook.GetSelection())
        self.notebook.DeletePage(self.notebook.GetSelection())
        
    def onLoadExample(self, name):
        self.editor = self.notebook.GetPage(self.notebook.GetSelection())
        self.notebook.SetPageText(self.notebook.GetSelection(), name)

    def onPageChange(self, event):
        firstWindow = self.editor.shel
        self.editor = self.notebook.GetPage(self.notebook.GetSelection())        
        self.splitter2.DetachWindow(firstWindow)
        firstWindow.Hide()
        self.splitter2.InsertWindow(1, self.editor.shel)
        if not self.editor.path:
            if self.editor.GetModify():
                self.SetTitle("*** Ounk editor ***")
            else:    
                self.SetTitle("Ounk editor")
        else:
            if self.editor.GetModify():
                self.SetTitle('*** ' + self.editor.path + ' ***')
            else:
                self.SetTitle(self.editor.path)
        self.controlPanel.checkButton()
        
    def onSashChanging(self, evt):
        pos = self.mainSplitter.GetSashPosition(0)
        self.controlPanel.tree.tree.SetSize((pos-13, -1))
        self.controlPanel.project.tree.SetSize((pos-13, -1))
        self.controlPanel.SetSize((pos, -1))
        self.editor.checkScrollbar()
        evt.Skip()
        
    def resize(self, size):
        self.X, self.Y = size[0], size[1]
        self.splitter2.SetSashPosition(0, size[1]*0.75)
        self.splitter.SetSashPosition(0, size[0]*0.6)
        self.mainSplitter.SetSashPosition(0, size[0]*.15)
        self.controlPanel.tree.tree.SetSize((size[0]*.15-13, size[1]-102))
        self.controlPanel.project.tree.SetSize((size[0]*.15-13, size[1]-102))
        
    def SetTitle(self, title):
        self.mainFrame.SetTitle(title)

    def GetTitle(self):
        return self.mainFrame.GetTitle()

    def OnQuit(self):
        for i in range(self.notebook.GetPageCount()):
            ed = self.notebook.GetPage(i)
            ed.Close()

class MyShell(py.shell.Shell):
    def __init__(self, parent, id, size=wx.Size(-1,-1), style=wx.SUNKEN_BORDER):
        py.shell.Shell.__init__(self, parent, id, size=wx.Size(-1,-1), style=wx.SUNKEN_BORDER, locals={})

        self.SetSTCCursor(2)

        self.wordlist = [subkey for key in LIB_INFO.keys() for subkey in LIB_INFO[key].keys()]
        self.alphaStr = string.lowercase + string.uppercase

        self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
        self.SetMarginWidth(1, 25)
        # Setup a margin to hold fold markers
        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
        self.SetMarginSensitive(2, True)
        self.SetMarginWidth(2, 12)
        
        self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)

        self.setStyle()

    def OnUpdateUI(self, evt):
        evt.Skip()
        
    def setStyle(self):
        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "fore:%(default)s,face:%(face)s,size:%(size)d,back:%(background)s" % faces)
        self.StyleClearAll()  # Reset all to be like the default

        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "fore:%(linenumber)s,back:%(marginback)s,face:%(face)s,size:%(size2)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "fore:%(default)s,face:%(face)s" % faces)
        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#000000,back:#888BFF,bold")
        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#AA0000,bold")

        # Python styles
        # Default
        self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
        # Comments
        self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:%(comment)s,face:%(face)s,size:%(size)d" % faces)
        # Number
        self.StyleSetSpec(stc.STC_P_NUMBER, "fore:%(number)s,face:%(face)s,bold,size:%(size)d" % faces)
        # String
        self.StyleSetSpec(stc.STC_P_STRING, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
        # Single quoted string
        self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
        # Keyword
        self.StyleSetSpec(stc.STC_P_WORD, "fore:%(keyword)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Triple quotes
        self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
        # Triple double quotes
        self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
        # Class name definition
        self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:%(class)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Function or method name definition
        self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:%(function)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d,face:%(face)s" % faces)
        # Identifiers
        self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:%(identifier)s,face:%(face)s,size:%(size)d" % faces)
        # Comment-blocks
        self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:%(commentblock)s,face:%(face)s,size:%(size)d" % faces)

        self.SetCaretForeground(faces['caret'])
    
    def showAutoComp(self):
        charBefore = None
        caretPos = self.GetCurrentPos()
        
        if caretPos > 0:
            charBefore = self.GetCharAt(caretPos - 1)
            
        # check if inside a ounklib function
        prevLine = self.GetCurrentLine()-1
        firstPos = self.GetLineEndPosition(prevLine) + 1    
        if caretPos > 0: 
            self.inside = False
            for i in range(firstPos, caretPos):
                if chr(self.GetCharAt(i)) == ")":
                    self.inside = False
                elif chr(self.GetCharAt(i)) == "(":
                    wordend = i
                    self.inside = True
                else:
                    continue
        else:
            self.inside = False   

        if self.inside:
            wordbegin = self.WordStartPosition(wordend, True)
            whichword = self.GetTextRange(wordbegin, wordend)

        startpos = self.WordStartPosition(caretPos, True)
        endpos = self.WordEndPosition(caretPos, True)
        currentword = self.GetTextRange(startpos, endpos)
                
        self.auto = False
        if self.inside and whichword in self.wordlist and chr(charBefore) not in "=, '\"[]{}.":
            list = ''
            arglist = inspect.getargspec(eval(whichword))[0]
            for arg in arglist:
                if arg.startswith(currentword) and arg != currentword:
                    list = list + arg + ' '
            if list:        
                self.AutoCompShow(len(currentword), list)
                self.auto = True
        elif chr(charBefore) in self.alphaStr:
            list = ''
            for word in self.wordlist:
                if word.startswith(currentword) and word != currentword:
                    list = list + word + ' ' 
            if list:
                self.AutoCompShow(len(currentword), list)
                self.auto = True
                
        selStartPos, selEndPos = self.GetSelection()
        self.firstLine = self.LineFromPosition(selStartPos)
        self.endLine = self.LineFromPosition(selEndPos)

class Editor(stc.StyledTextCtrl):
    def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style= wx.NO_BORDER,
                 infoView=None, setTitle=None, getTitle=None, splitter2=None, checkButton=None, enableFunctionManual=None):
        stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)

        dt = MyFileDropTarget(self)
        self.SetDropTarget(dt)
        
        self.SetSTCCursor(2)
        self.panel = parent
        self.checkButton = checkButton
        self.enableFunctionManual = enableFunctionManual
        
        self.infoView = infoView
        self.shel = MyShell(splitter2, -1, size=(0,-1))  
        self.shel.push('from ounk.ounklib import *', True)
        self.inRun = False
        self.procNum = None
        self.path = ''
        self.style = 'Python'
        self.setTitle = setTitle
        self.getTitle = getTitle
        self.saveMark = False        
        self.inside = False
        self.anchor1 = self.anchor2 = 0
        
        self.SetForegroundColour(COLOUR)

        self.alphaStr = string.lowercase + string.uppercase + '0123456789'

        self.Colourise(0, -1)
        self.SetCurrentPos(0)
        
        self.SetIndent(4)
        self.SetTabIndents(True)
        self.SetTabWidth(4)
        self.SetUseTabs(False)

        self.EmptyUndoBuffer()
        self.SetFocus()
        
        self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
        self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
        self.CmdKeyAssign(ord('Z'), stc.STC_SCMOD_SHIFT+stc.STC_SCMOD_CTRL, stc.STC_CMD_REDO)

        self.SetProperty("fold", "1")
        self.SetProperty("tab.timmy.whinge.level", "1")
        self.SetMargins(5,5)
        self.SetViewWhiteSpace(False)
        self.SetUseAntiAliasing(True)
        self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
        self.SetEdgeColumn(1000)
        
        self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
        self.SetMarginWidth(1, 25)
        # Setup a margin to hold fold markers
        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
        self.SetMarginSensitive(2, True)
        self.SetMarginWidth(2, 12)

        self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
        self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_TIMER, self.onTimer)

        self.Bind(wx.EVT_FIND, self.OnFind)
        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
        self.Bind(wx.EVT_FIND_REPLACE, self.OnFind)
        self.Bind(wx.EVT_FIND_REPLACE_ALL, self.OnFind)
        self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
        
        self.setStyle()
        self.Refresh()

    ### Style ###
    def setStyle(self):
        split = os.path.split(self.path)[1].split('.')
        if len(split) > 1:
            if split[1] == 'csd' or split[1] == 'txt':
                self.style = 'Csound'
            else:
                self.style = 'Python'
        else:
            self.style = 'Python'

        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_CIRCLEMINUS, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_CIRCLEPLUS, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNERCURVE, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_CIRCLEPLUSCONNECTED, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, faces['markerfg'], faces['markerbg'])
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, faces['markerfg'], faces['markerbg'])   

        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "fore:%(default)s,face:%(face)s,size:%(size)d,back:%(background)s" % faces)
        self.StyleClearAll()  # Reset all to be like the default

        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "fore:%(linenumber)s,back:%(marginback)s,face:%(face)s,size:%(size2)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "fore:%(default)s,face:%(face)s" % faces)
        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#000000,back:#888BFF,bold")
        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#AA0000,bold")

        # Python styles
        if self.style == 'Python':
            self.wordlist = [subkey for key in LIB_INFO.keys() for subkey in LIB_INFO[key].keys()]
            self.SetLexer(stc.STC_LEX_PYTHON)
            self.SetKeyWords(0, " ".join(keyword.kwlist) + " None True False " + " ".join(self.wordlist))
    
            # Default
            self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
            # Comments
            self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:%(comment)s,face:%(face)s,size:%(size)d" % faces)
            # Number
            self.StyleSetSpec(stc.STC_P_NUMBER, "fore:%(number)s,face:%(face)s,bold,size:%(size)d" % faces)
            # String
            self.StyleSetSpec(stc.STC_P_STRING, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
            # Single quoted string
            self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
            # Keyword
            self.StyleSetSpec(stc.STC_P_WORD, "fore:%(keyword)s,face:%(face)s,bold,size:%(size)d" % faces)
            # Triple quotes
            self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
            # Triple double quotes
            self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
            # Class name definition
            self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:%(class)s,face:%(face)s,bold,size:%(size)d" % faces)
            # Function or method name definition
            self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:%(function)s,face:%(face)s,bold,size:%(size)d" % faces)
            # Operators
            self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d,face:%(face)s" % faces)
            # Identifiers
            self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:%(identifier)s,face:%(face)s,size:%(size)d" % faces)
            # Comment-blocks
            self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:%(commentblock)s,face:%(face)s,size:%(size)d" % faces)
        # Csound styles
        else:
            keywordfile = open(os.path.join(RESOURCES_PATH, "opcodes.txt"), "r")
            keywordlist = []
            for line in keywordfile.readlines():
                for word in line.split():
                    keywordlist.append(word)
            keywordfile.close()
            self.SetLexer(stc.STC_LEX_CSOUND)
            self.SetKeyWords(0, " ".join(keywordlist))
            # Default
            self.StyleSetSpec(stc.STC_CSOUND_DEFAULT, "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
            # Comments
            self.StyleSetSpec(stc.STC_CSOUND_COMMENT, "fore:#50DD13,face:%(face)s,size:%(size)d" % faces)
            # Keyword
            self.StyleSetSpec(stc.STC_CSOUND_OPCODE, "fore:%(keyword)s,face:%(face)s,bold,size:%(size)d" % faces)
            
        self.SetCaretForeground(faces['caret'])

    ### Find and Replace ###
    def OnShowFindReplace(self):
        data = wx.FindReplaceData()
        self.findReplace = wx.FindReplaceDialog(self, data, "Find & Replace", wx.FR_REPLACEDIALOG | wx.FR_NOUPDOWN)
        self.findReplace.data = data  # save a reference to it...
        self.findReplace.Show(True)

    def OnFind(self, evt):
        map = { wx.wxEVT_COMMAND_FIND : "FIND",
                wx.wxEVT_COMMAND_FIND_NEXT : "FIND_NEXT",
                wx.wxEVT_COMMAND_FIND_REPLACE : "REPLACE",
                wx.wxEVT_COMMAND_FIND_REPLACE_ALL : "REPLACE_ALL" }

        et = evt.GetEventType()

        findTxt = evt.GetFindString()

        selection = self.GetSelection()
        if selection[0] == selection[1]:
            selection = (0, self.GetLength())

        if map[et] == 'FIND':
            startpos = self.FindText(selection[0], selection[1], findTxt, evt.GetFlags())
            endpos = startpos+len(findTxt)
            self.anchor1 = endpos
            self.anchor2 = selection[1]
            self.SetSelection(startpos, endpos)  
        elif map[et] == 'FIND_NEXT':
            startpos = self.FindText(self.anchor1, self.anchor2, findTxt, evt.GetFlags())
            endpos = startpos+len(findTxt)
            self.anchor1 = endpos
            self.SetSelection(startpos, endpos)
        elif map[et] == 'REPLACE':
            startpos = self.FindText(selection[0], selection[1], findTxt)
            endpos = startpos+len(findTxt)
            if startpos != -1:
                self.SetSelection(startpos, endpos)
                self.ReplaceSelection(evt.GetReplaceString())
        elif map[et] == 'REPLACE_ALL':
            self.anchor1 = selection[0]
            self.anchor2 = selection[1]
            startpos = selection[0]
            while startpos != -1:
                startpos = self.FindText(self.anchor1, self.anchor2, findTxt)
                endpos = startpos+len(findTxt)
                self.anchor1 = endpos
                if startpos != -1:
                    self.SetSelection(startpos, endpos)
                    self.ReplaceSelection(evt.GetReplaceString())

    def OnFindClose(self, evt):
        evt.GetDialog().Destroy()

    ### Timer to check if Csound is running for this editor ###        
    def startTimer(self):
        self.inRun = True
        self.timer = wx.Timer(self)
        self.timer.Start(100)  

    def stopTimer(self): 
        if self.inRun:
            if getPid(self.procNum):
                stopCsound(self.procNum)
            self.inRun = False
            self.shel.clearHistory() 
            if self.IsShownOnScreen():
                self.checkButton()
            self.timer.Stop()
            del self.timer  

    def onTimer(self, evt):
        if not checkCsound(self.procNum): self.stopTimer()

    ### Save and Close file ###
    def saveMyFile(self, file):
        self.SaveFile(file)
        self.path = file
        self.saveMark = False

    def close(self):
        if self.GetModify():
            if not self.path: f = "Untitled"
            else: f = self.path
            dlg = wx.MessageDialog(None, 'file ' + f + ' has been modified. Do you want to save?', 'Warning!', wx.YES | wx.NO | wx.CANCEL)
            but = dlg.ShowModal()
            if but == wx.ID_YES:
                dlg.Destroy()
                if not self.path:
                    dlg2 = wx.FileDialog(None, message="Save file as ...", defaultDir=os.getcwd(),
                        defaultFile="", style=wx.SAVE)
                    dlg2.SetFilterIndex(0)
            
                    if dlg2.ShowModal() == wx.ID_OK:
                        path = dlg2.GetPath()
                        self.SaveFile(path)
                        dlg2.Destroy()
                    else:
                        dlg2.Destroy()
                        return 'keep'
                else:        
                    self.SaveFile(self.path)
                return 'delete'    
            elif but == wx.ID_NO:
                dlg.Destroy()
                return 'delete'
            elif but == wx.ID_CANCEL:
                dlg.Destroy()
                return 'keep'
        else:
            return 'delete'
            
    def OnClose(self, event):
        if self.GetModify():
            if not self.path: f = "Untitled"
            else: f = self.path
            dlg = wx.MessageDialog(None, 'file ' + f + ' has been modified. Do you want to save?', 'Warning!', wx.YES | wx.NO)
            if dlg.ShowModal() == wx.ID_YES:
                dlg.Destroy()
                if not self.path:
                    dlg2 = wx.FileDialog(None, message="Save file as ...", defaultDir=os.getcwd(),
                        defaultFile="", style=wx.SAVE)
                    dlg2.SetFilterIndex(0)
            
                    if dlg2.ShowModal() == wx.ID_OK:
                        path = dlg2.GetPath()
                        self.SaveFile(path)
                        dlg2.Destroy()
                else:        
                    self.SaveFile(self.path)
            else:
                dlg.Destroy()

    def OnModified(self):
        if self.GetModify() and not self.saveMark:
            title = self.getTitle()
            str = '*** ' + title + ' ***'
            self.setTitle(str)
            self.saveMark = True

    ### Editor functions ###        
    def showAutoComp(self):
        charBefore = None
        caretPos = self.GetCurrentPos()
        
        if caretPos > 0:
            charBefore = self.GetCharAt(caretPos - 1)
            
        # check if inside a ounklib function
        prevLine = self.GetCurrentLine()-1
        firstPos = self.GetLineEndPosition(prevLine) + 1    
        if caretPos > 0: 
            self.inside = False
            for i in range(firstPos, caretPos):
                if chr(self.GetCharAt(i)) == ")":
                    self.inside = False
                elif chr(self.GetCharAt(i)) == "(":
                    wordend = i
                    self.inside = True
                else:
                    continue
        else:
            self.inside = False   

        if self.inside:
            wordbegin = self.WordStartPosition(wordend, True)
            whichword = self.GetTextRange(wordbegin, wordend)

        startpos = self.WordStartPosition(caretPos, True)
        endpos = self.WordEndPosition(caretPos, True)
        currentword = self.GetTextRange(startpos, endpos)
                
        self.auto = False # sert a rien!
        if self.inside and whichword in self.wordlist and chr(charBefore) not in "=, '\"[]{}.":
            list = ''
            arglist = inspect.getargspec(eval(whichword))[0]
            for arg in arglist:
                if arg.startswith(currentword) and arg != currentword:
                    list = list + arg + ' '
            if list:        
                self.AutoCompShow(len(currentword), list)
                self.auto = True
        elif chr(charBefore) in self.alphaStr:
            list = ''
            for word in self.wordlist:
                if word.startswith(currentword) and word != currentword:
                    list = list + word + ' ' 
            if list:
                self.AutoCompShow(len(currentword), list)
                self.auto = True
                        
    def OnUpdateUI(self, evt):
        # check for matching braces
        braceAtCaret = -1
        braceOpposite = -1
        charBefore = None
        caretPos = self.GetCurrentPos()

        if caretPos > 0:
            charBefore = self.GetCharAt(caretPos - 1)
            styleBefore = self.GetStyleAt(caretPos - 1)

        # check before
        if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
            braceAtCaret = caretPos - 1

        # check after
        if braceAtCaret < 0:
            charAfter = self.GetCharAt(caretPos)
            styleAfter = self.GetStyleAt(caretPos)
            
            if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
                braceAtCaret = caretPos    
        if braceAtCaret >= 0:
            braceOpposite = self.BraceMatch(braceAtCaret)
            
        if braceAtCaret != -1  and braceOpposite == -1:
            self.BraceBadLight(braceAtCaret)
        else:
            self.BraceHighlight(braceAtCaret, braceOpposite)

        startpos = self.WordStartPosition(caretPos, True)
        endpos = self.WordEndPosition(caretPos, True)
        currentword = self.GetTextRange(startpos, endpos)

        # check if inside a ounklib function
        prevLine = self.GetCurrentLine()-1
        firstPos = self.GetLineEndPosition(prevLine) + 1    
        if caretPos > 0: 
            inside = False
            for i in range(firstPos, caretPos):
                if chr(self.GetCharAt(i)) == ")":
                    inside = False
                elif chr(self.GetCharAt(i)) == "(":
                    wordend = i
                    inside = True
                else:
                    continue
        else:
            inside = False

        if not inside:
            for key in LIB_INFO.keys():
                if LIB_INFO[key].has_key(currentword):
                    self.infoView.changeText(key, currentword)
                    break

        if self.GetSelectedText() in self.wordlist:
            self.enableFunctionManual(True)
        else:    
            self.enableFunctionManual(False)

        self.checkScrollbar()            
        self.OnModified()
        
        evt.Skip()

    def checkScrollbar(self):        
        lineslength = [self.LineLength(i)+1 for i in range(self.GetLineCount())]
        maxlength = max(lineslength)
        width = self.GetCharWidth() + (self.GetZoom() * 0.5)
        
        if (self.GetSize()[0]) < (maxlength * width):
            self.SetUseHorizontalScrollBar(True)
        else:    
            self.SetUseHorizontalScrollBar(False)
            self.SetXOffset(0)

    def OnComment(self):
        selStartPos, selEndPos = self.GetSelection()
        self.firstLine = self.LineFromPosition(selStartPos)
        self.endLine = self.LineFromPosition(selEndPos)

        if self.style == 'Csound':
            commentStr = ';'
        else:
            commentStr = '#'

        for i in range(self.firstLine, self.endLine+1):
            lineLen = len(self.GetLine(i))
            pos = self.PositionFromLine(i)
            if self.GetTextRange(pos,pos+1) != commentStr and lineLen > 2:
                self.InsertText(pos, commentStr)
            elif self.GetTextRange(pos,pos+1) == commentStr:
                self.GotoPos(pos+1)
                self.DelWordLeft()
        
    def OnMarginClick(self, evt):
        # fold and unfold as needed
        if evt.GetMargin() == 2:
            if evt.GetShift() and evt.GetControl():
                self.FoldAll()
            else:
                lineClicked = self.LineFromPosition(evt.GetPosition())

                if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
                    if evt.GetShift():
                        self.SetFoldExpanded(lineClicked, True)
                        self.Expand(lineClicked, True, True, 1)
                    elif evt.GetControl():
                        if self.GetFoldExpanded(lineClicked):
                            self.SetFoldExpanded(lineClicked, False)
                            self.Expand(lineClicked, False, True, 0)
                        else:
                            self.SetFoldExpanded(lineClicked, True)
                            self.Expand(lineClicked, True, True, 100)
                    else:
                        self.ToggleFold(lineClicked)

    def FoldAll(self):
        lineCount = self.GetLineCount()
        expanding = True

        # find out if we are folding or unfolding
        for lineNum in range(lineCount):
            if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
                expanding = not self.GetFoldExpanded(lineNum)
                break

        lineNum = 0

        while lineNum < lineCount:
            level = self.GetFoldLevel(lineNum)
            if level & stc.STC_FOLDLEVELHEADERFLAG and \
               (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:

                if expanding:
                    self.SetFoldExpanded(lineNum, True)
                    lineNum = self.Expand(lineNum, True)
                    lineNum = lineNum - 1
                else:
                    lastChild = self.GetLastChild(lineNum, -1)
                    self.SetFoldExpanded(lineNum, False)

                    if lastChild > lineNum:
                        self.HideLines(lineNum+1, lastChild)
            lineNum = lineNum + 1
        
    def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
        lastChild = self.GetLastChild(line, level)
        line = line + 1

        while line <= lastChild:
            if force:
                if visLevels > 0:
                    self.ShowLines(line, line)
                else:
                    self.HideLines(line, line)
            else:
                if doExpand:
                    self.ShowLines(line, line)

            if level == -1:
                level = self.GetFoldLevel(line)

            if level & stc.STC_FOLDLEVELHEADERFLAG:
                if force:
                    if visLevels > 1:
                        self.SetFoldExpanded(line, True)
                    else:
                        self.SetFoldExpanded(line, False)

                    line = self.Expand(line, doExpand, force, visLevels-1)

                else:
                    if doExpand and self.GetFoldExpanded(line):
                        line = self.Expand(line, True, force, visLevels-1)
                    else:
                        line = self.Expand(line, False, force, visLevels-1)
            else:
                line = line + 1
        return line

class InfoView(stc.StyledTextCtrl):
    def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
        stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)

        self.SetSTCCursor(2)
        
        self.SetCaretWidth(0)
        self.SetWrapMode(True)
        self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
        self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)

        self.SetLexer(stc.STC_LEX_PYTHON)
        myKeywordsList = [subkey for key in LIB_INFO.keys() for subkey in LIB_INFO[key].keys()]
        self.SetKeyWords(0, "None True False " + " ".join(myKeywordsList) + " ".join(LIB_DOC.keys()))

        self.SetProperty("tab.timmy.whinge.level", "1")
        self.SetMargins(5,5)
        self.SetViewWhiteSpace(False)
        self.SetUseAntiAliasing(True)
        self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
        self.SetEdgeColumn(600)

        self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
        self.SetMarginWidth(1, 0)
        
        self.setStyle()
        
    def setStyle(self):
        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "fore:%(default)s,face:%(face)s,size:%(size)d,back:%(background)s" % faces)
        self.StyleClearAll()  # Reset all to be like the default

        # Python styles
        # Default
        self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:%(default)s,face:%(face)s,size:%(size)d" % faces)
        # Comments
        self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:%(comment)s,face:%(face)s,size:%(size)d" % faces)
        # Number
        self.StyleSetSpec(stc.STC_P_NUMBER, "fore:%(number)s,face:%(face)s,bold,size:%(size)d" % faces)
        # String
        self.StyleSetSpec(stc.STC_P_STRING, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
        # Single quoted string
        self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:%(string)s,face:%(face)s,size:%(size)d" % faces)
        # Keyword
        self.StyleSetSpec(stc.STC_P_WORD, "fore:%(keyword)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Triple quotes
        self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
        # Triple double quotes
        self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:%(triple)s,face:%(face)s,size:%(size)d" % faces)
        # Class name definition
        self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:%(class)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Function or method name definition
        self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:%(function)s,face:%(face)s,bold,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d,face:%(face)s" % faces)
        # Identifiers
        self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:%(identifier)s,face:%(face)s,size:%(size)d" % faces)
        # Comment-blocks
        self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:%(commentblock)s,face:%(face)s,size:%(size)d" % faces)
    
    def changeText(self, key, currentword):       
        self.ClearAll()
        if key != None:
            self.AddText('%s:\n' % currentword + LIB_INFO[key][currentword].__doc__.split('--- PARAMETERS ---')[0])
        else:
            self.AddText('%s:\n' % currentword + LIB_DOC[currentword])
        self.SetViewEOL(False)
        
class FunctionTree(wx.Panel):
    def __init__(self, parent, infoView, mainPanel, size):
        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS | wx.SUNKEN_BORDER | wx.EXPAND)

        self.mainPanel = mainPanel
        self.infoView = infoView

        size = size[0], size[1]-10
        self.tree = wx.TreeCtrl(self, -1, (0, 0), size,
                               wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.SUNKEN_BORDER | wx.EXPAND)
                               
        if wx.Platform == '__WXMAC__':
            self.tree.SetFont(wx.Font(11, wx.ROMAN, wx.NORMAL, wx.NORMAL, face=faces['face']))
        else:
            self.tree.SetFont(wx.Font(8, wx.ROMAN, wx.NORMAL, wx.NORMAL, face=faces['face']))
        isz = (12,12)
        il = wx.ImageList(isz[0], isz[1])
        fldridx     = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER,      wx.ART_OTHER, isz))
        fldropenidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, isz))
        fileidx     = il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))

        self.tree.SetImageList(il)
        self.il = il

        self.root = self.tree.AddRoot("Library")
        self.tree.SetPyData(self.root, None)
        self.tree.SetItemImage(self.root, fldridx, wx.TreeItemIcon_Normal)
        self.tree.SetItemImage(self.root, fldropenidx, wx.TreeItemIcon_Expanded)
        
        for key in LIB_INFO.keys():
            child = self.tree.AppendItem(self.root, "%s" % key)
            self.tree.SetPyData(child, None)
            self.tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
            self.tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)
            
            for subkey in LIB_INFO[key].keys():
                item = self.tree.AppendItem(child, "%s" % subkey)
                self.tree.SetPyData(item, None)
                self.tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Normal)
                self.tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Selected)
            self.tree.SortChildren(self.root)
            self.tree.SortChildren(child)

        child = self.tree.AppendItem(self.root, "Examples")
        self.tree.SetPyData(child, None)
        self.tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
        self.tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)
        files = [file.split('.')[0] for file in os.listdir(os.path.join(DOC_PATH, 'examples')) if file[0] != '.']
        for file in files:
            item = self.tree.AppendItem(child, file)
            self.tree.SetPyData(item, None)
            self.tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Normal)
            self.tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Selected)
        self.tree.SortChildren(child)
        self.tree.SetSpacing(12)
        self.tree.SetIndent(6)
        
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.tree)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndEdit, self.tree)

        self.tree.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
        
        self.setStyle()
        
    def setStyle(self):
        self.tree.SetBackgroundColour(faces['background'])
        self.tree.SetItemTextColour(self.root, faces['identifier'])
        (child, cookie) = self.tree.GetFirstChild(self.root)
        
        while child.IsOk():
            self.tree.SetItemTextColour(child, faces['identifier'])
            if self.tree.ItemHasChildren(child):
                (subchild, subcookie) = self.tree.GetFirstChild(child)
                while subchild.IsOk():
                    self.tree.SetItemTextColour(subchild, faces['identifier'])
                    (subchild, subcookie) = self.tree.GetNextChild(child, subcookie)
            (child, cookie) = self.tree.GetNextChild(self.root, cookie)
            
    def OnBeginEdit(self, event):
        # show how to prevent edit...
        item = event.GetItem()
        if item:
            wx.Bell()

            # Lets just see what's visible of its children
            cookie = 0
            root = event.GetItem()
            (child, cookie) = self.tree.GetFirstChild(root)

            while child.IsOk():
                (child, cookie) = self.tree.GetNextChild(root, cookie)
            event.Veto()

    def OnEndEdit(self, event):
        # show how to reject edit, we'll not allow any digits
        for x in event.GetLabel():
            if x in string.digits:
                event.Veto()
                return

    def OnLeftDClick(self, event):
        pt = event.GetPosition();
        item, flags = self.tree.HitTest(pt)
        if item:
            hasChild = self.tree.ItemHasChildren(item)
            if not hasChild:
                parent = self.tree.GetItemParent(item)
                if self.tree.GetItemText(parent) == 'Examples':
                    self.mainPanel.addNewPage()
                    filepath = os.path.join(DOC_PATH, 'examples', self.tree.GetItemText(item) + '.py')
                    if os.path.isfile(filepath):
                        self.mainPanel.editor.LoadFile(filepath)
                    else:
                        filepath = os.path.join(DOC_PATH, 'examples', self.tree.GetItemText(item) + '.txt')
                        self.mainPanel.editor.LoadFile(filepath)
                    self.mainPanel.onLoadExample(self.tree.GetItemText(item))
                    self.mainPanel.SetTitle('Ounk Editor')
                    self.mainPanel.editor.path = ''
                    self.mainPanel.editor.saveMark = False
                else:    
                    self.item = self.tree.GetItemText(item)
                    self.mainPanel.editor.AddText(self.item + '(')
                    self.mainPanel.editor.SetFocus()
        event.Skip()
        
    def OnSelChanged(self, event):
        self.item = self.tree.GetItemText(event.GetItem())
        for key in LIB_INFO.keys():
            if LIB_INFO[key].has_key(self.item):
                self.infoView.changeText(key, self.item)
        if LIB_DOC.has_key(self.item):
            self.infoView.changeText(None, self.item)
        event.Skip()

class ProjectTree(wx.Panel):
    """Projects panel"""
    def __init__(self, parent, mainPanel, size):
        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS | wx.SUNKEN_BORDER | wx.EXPAND)

        self.mainPanel = mainPanel

        self.projectDict = {}
        
        size = size[0], size[1]-10
        self.tree = wx.TreeCtrl(self, -1, (0, 0), size,
                               wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.SUNKEN_BORDER | wx.EXPAND)

        if wx.Platform == '__WXMAC__':
            self.tree.SetFont(wx.Font(11, wx.ROMAN, wx.NORMAL, wx.NORMAL, face=faces['face']))
        else:
            self.tree.SetFont(wx.Font(8, wx.ROMAN, wx.NORMAL, wx.NORMAL, face=faces['face']))
        self.tree.SetBackgroundColour(faces['background'])
        
        isz = (12,12)
        self.il = wx.ImageList(isz[0], isz[1])
        self.fldridx     = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER,      wx.ART_OTHER, isz))
        self.fldropenidx = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, isz))
        self.fileidx     = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))

        self.tree.SetImageList(self.il)
        self.tree.SetSpacing(12)
        self.tree.SetIndent(6)

        self.root = self.tree.AddRoot("Projects")
        self.tree.SetPyData(self.root, None)
        self.tree.SetItemImage(self.root, self.fldridx, wx.TreeItemIcon_Normal)
        self.tree.SetItemImage(self.root, self.fldropenidx, wx.TreeItemIcon_Expanded)
        self.tree.SetItemTextColour(self.root, faces['identifier'])
        
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.tree)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndEdit, self.tree)

        self.tree.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)

    def setStyle(self):
        if not self.tree.IsEmpty():
            self.tree.SetBackgroundColour(faces['background'])
            self.tree.SetItemTextColour(self.root, faces['identifier'])
            (child, cookie) = self.tree.GetFirstChild(self.root)
            
            while child.IsOk():
                self.tree.SetItemTextColour(child, faces['identifier'])
                if self.tree.ItemHasChildren(child):
                    (subchild, subcookie) = self.tree.GetFirstChild(child)
                    while subchild.IsOk():
                        self.tree.SetItemTextColour(subchild, faces['identifier'])
                        if self.tree.ItemHasChildren(subchild):
                            (ssubchild, ssubcookie) = self.tree.GetFirstChild(subchild)
                            while ssubchild.IsOk():
                                self.tree.SetItemTextColour(ssubchild, faces['identifier'])
                                (ssubchild, ssubcookie) = self.tree.GetNextChild(subchild, ssubcookie)
                                if self.tree.ItemHasChildren(ssubchild):
                                    while sssubchild.IsOk():
                                        self.tree.SetItemTextColour(sssubchild, faces['identifier'])
                                        (sssubchild, sssubcookie) = self.tree.GetNextChild(ssubchild, sssubcookie)
                            (sssubchild, sssubcookie) = self.tree.GetFirstChild(ssubchild)              
                        (subchild, subcookie) = self.tree.GetNextChild(child, subcookie)          
                (child, cookie) = self.tree.GetNextChild(self.root, cookie)
            
    def loadProject(self, dirPath):
        projectName = os.path.split(dirPath)[1]

        self.projectDict[projectName] = dirPath

        projectDir = {}

        for root, dirs, files in os.walk(dirPath):
            if os.path.split(root)[1][0] != '.':
                if root == dirPath:          
                    child = self.tree.AppendItem(self.root, projectName)
                    self.tree.SetPyData(child, None)
                    self.tree.SetItemImage(child, self.fldridx, wx.TreeItemIcon_Normal)
                    self.tree.SetItemImage(child, self.fldropenidx, wx.TreeItemIcon_Expanded)
                    self.tree.SetItemTextColour(child, faces['identifier'])
                    if dirs:
                        ddirs = [dir for dir in dirs if dir[0] != '.']
                        for dir in ddirs:
                            subfol = self.tree.AppendItem(child, "%s" % dir)
                            projectDir[dir] = subfol
                            self.tree.SetPyData(subfol, None)
                            self.tree.SetItemImage(subfol, self.fldridx, wx.TreeItemIcon_Normal)
                            self.tree.SetItemImage(subfol, self.fldropenidx, wx.TreeItemIcon_Expanded)
                            self.tree.SetItemTextColour(subfol, faces['identifier'])
                    if files:
                        ffiles = [file for file in files if file[0] != '.' and file[-3:] != 'pyc']
                        for file in ffiles:
                            item = self.tree.AppendItem(child, "%s" % file)
                            self.tree.SetPyData(item, None)
                            self.tree.SetItemImage(item, self.fileidx, wx.TreeItemIcon_Normal)
                            self.tree.SetItemImage(item, self.fileidx, wx.TreeItemIcon_Selected)
                            self.tree.SetItemTextColour(item, faces['identifier'])
                else:
                    if os.path.split(root)[1] in projectDir.keys():
                        parent = projectDir[os.path.split(root)[1]]
                        if dirs:
                            ddirs = [dir for dir in dirs if dir[0] != '.']
                            for dir in ddirs:
                                subfol = self.tree.AppendItem(parent, "%s" % dir)
                                projectDir[dir] = subfol
                                self.tree.SetPyData(subfol, None)
                                self.tree.SetItemImage(subfol, self.fldridx, wx.TreeItemIcon_Normal)
                                self.tree.SetItemImage(subfol, self.fldropenidx, wx.TreeItemIcon_Expanded)  
                                self.tree.SetItemTextColour(subfol, faces['identifier'])
                        if files:
                            ffiles = [file for file in files if file[0] != '.' and file[-3:] != 'pyc']
                            for file in ffiles:
                                item = self.tree.AppendItem(parent, "%s" % file)
                                self.tree.SetPyData(item, None)
                                self.tree.SetItemImage(item, self.fileidx, wx.TreeItemIcon_Normal)
                                self.tree.SetItemImage(item, self.fileidx, wx.TreeItemIcon_Selected)
                                self.tree.SetItemTextColour(item, faces['identifier'])
                        
        self.tree.SortChildren(self.root)
        self.tree.SortChildren(child)
  
    def OnBeginEdit(self, event):
        # show how to prevent edit...
        item = event.GetItem()
        if item:
            wx.Bell()
            # Lets just see what's visible of its children
            cookie = 0
            root = event.GetItem()
            (child, cookie) = self.tree.GetFirstChild(root)
            while child.IsOk():
                (child, cookie) = self.tree.GetNextChild(root, cookie)
            event.Veto()

    def OnEndEdit(self, event):
        # show how to reject edit, we'll not allow any digits
        for x in event.GetLabel():
            if x in string.digits:
                event.Veto()
                return

    def OnLeftDClick(self, event):
        pt = event.GetPosition()
        item, flags = self.tree.HitTest(pt)
        if item:
            hasChild = self.tree.ItemHasChildren(item)
            if not hasChild:
                parent = None
                ritem = item
                while self.tree.GetItemParent(ritem) != self.tree.GetRootItem():
                    ritem = self.tree.GetItemParent(ritem)
                    parent = self.tree.GetItemText(ritem)
                dirPath = self.projectDict[parent]
                for root, dirs, files in os.walk(dirPath):
                    if files:
                        for file in files:
                            if file == self.tree.GetItemText(item):
                                path = os.path.join(root, file)
                if str(os.path.split(path)[1].split('.')[1]) in ['aif', 'aiff', 'AIF', 'AIFF', 'wav', 'WAV']:
                    if wx.Platform == '__WXMSW__':
                        os.system('start %s %s' % (conf['audioEditor'], path))
                    elif wx.Platform == '__WXMAC__':    
                        os.system('open -a "%s" %s' % (conf['audioEditor'], path))
                    else:
                        os.system('%s %s' % (conf['audioEditor'], path))
                else:
                    self.mainPanel.addPage(path)
                    self.mainPanel.editor.saveMark = False
        event.Skip()
        
    def OnSelChanged(self, event):
        self.item = self.tree.GetItemText(event.GetItem())
        for key in LIB_INFO.keys():
            if LIB_INFO[key].has_key(self.item):
                self.infoView.ClearAll()
                self.infoView.SetText('%s:\n' % self.item + LIB_INFO[key][self.item].__doc__)
        event.Skip()

class ManualPage(wx.Frame):
    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)

        menuBar = wx.MenuBar()
        menu = wx.Menu()
        menu.Append(1, "Close\tCtrl-W", "Close this window")
        self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=1)
        menuBar.Append(menu, "&File")
        self.SetMenuBar(menuBar)
            
        self.html1 = wx.html.HtmlWindow(self, -1, size=(800,600))
        htmlfile = DOC_PATH + '/html_man/Ounk_man/index.html'
        self.html1.LoadPage(htmlfile)
        self.SetFocus()

    def openPage(self, file):
        self.html1.LoadPage(file + '.html')

    def OnTimeToClose(self, evt):
        self.Destroy()

class LogPage(wx.Frame):
    def __init__(self, parent, title, pos, size, msg):
        wx.Frame.__init__(self, parent, -1, title, pos=pos, size=size)        

        menuBar = wx.MenuBar()
        menu = wx.Menu()
        menu.Append(1, "Save...\tCtrl-S", "Save...")
        self.Bind(wx.EVT_MENU, self.saveas, id=1)
        menu.Append(2, "Close\tCtrl-W", "Close this window")
        self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=2)
        menuBar.Append(menu, "&File")            
        self.SetMenuBar(menuBar)

        self.msg = msg
        self.text = wx.richtext.RichTextCtrl(self, style=wx.VSCROLL|wx.HSCROLL|wx.SUNKEN_BORDER)
        self.text.Freeze()
        self.text.WriteText(msg)
        self.text.Thaw()

        self.Show()
        self.SetFocus()
     
    def OnTimeToClose(self, evt):
        self.Destroy()

    def saveas(self, event):
        dlg = wx.FileDialog(self, message="Save file as ...", defaultDir=os.getcwd(),
            defaultFile="", style=wx.SAVE)
        dlg.SetFilterIndex(0)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            f = open(path, 'w')
            f.write(self.msg)
            f.close()
        dlg.Destroy()

class MyNotebook(wx.aui.AuiNotebook):
    def __init__(self, parent, size=(0,-1), style=wx.aui.AUI_NB_TAB_FIXED_WIDTH | 
                                            wx.aui.AUI_NB_CLOSE_ON_ALL_TABS |  
                                            wx.aui.AUI_NB_SCROLL_BUTTONS | wx.SUNKEN_BORDER):
        wx.aui.AuiNotebook.__init__(self, parent, size=size, style=style)
        dt = MyFileDropTarget(self)
        self.SetDropTarget(dt)            
            
class MyFileDropTarget(wx.FileDropTarget):
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    def OnDropFiles(self, x, y, filenames):
        for file in filenames:
            if os.path.isdir(file):
                self.window.GetTopLevelParent().panel.controlPanel.project.loadProject(file)
                sys.path.append(file)
                projectPath(file)
            elif os.path.isfile(file):    
                self.window.GetTopLevelParent().panel.addPage(file)
            else:
                pass
                
class MySplashScreen(wx.SplashScreen):
    def __init__(self):
        img = os.path.join(RESOURCES_PATH, 'splash.png')
        bmp = wx.Image(img).ConvertToBitmap()
        wx.SplashScreen.__init__(self, bmp, wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT, 4000, None, -1)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.fc = wx.FutureCall(2000, self.ShowMain)

    def OnClose(self, evt):
        # Make sure the default handler runs too so this window gets destroyed
        evt.Skip()
        self.Hide()
        
        # if the timer is still running then go ahead and show the main frame now
        if self.fc.IsRunning():
            self.fc.Stop()
            self.ShowMain()

    def ShowMain(self):
        X,Y = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
        print '%s %s by Olivier Belanger' % (NAME, VERSION)
        print 'Screen size: ', X,Y
        print 'Current working directory: ', os.getcwd()
        frame = MainFrame(None, -1, title='Ounk editor', size=(X-20, Y-50))
        frame.Show()
        if self.fc.IsRunning():
            self.Raise()

if __name__ == '__main__':
    try:
        import psyco
        psyco.full()
        print "Ounk Uses Psyco"
    except ImportError:
        print "Psyco not found"

    filesToOpen = []
    projectsToOpen = []
    if len(sys.argv) > 1:
        for f in sys.argv[1:]:
            if os.path.isdir(f):
                if f[-1] == '/': f = f[:-1]
                projectsToOpen.append(f)
            elif os.path.isfile(f):    
                filesToOpen.append(f)
            else:
                pass
                
    app = wx.PySimpleApp()
    sp = MySplashScreen()
    app.MainLoop()
